﻿using GodSharp.Communications.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace GodSharp.Communications
{
    /// <summary>
    /// CommunicationManager
    /// </summary>
    /// <seealso cref="GodSharp.Communications.Abstractions.ICommunicationManager" />
    public sealed class CommunicationManager : ICommunicationManager
    {
        /// <summary>
        /// Gets or sets the providers.
        /// </summary>
        /// <value>
        /// The providers.
        /// </value>
        public Dictionary<string, ICommunicationProvider> providers { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CommunicationManager"/> class.
        /// </summary>
        public CommunicationManager()
        {
            providers = new Dictionary<string, ICommunicationProvider>();
        }

        /// <summary>
        /// Adds the provider.
        /// </summary>
        /// <param name="provider">The provider.</param>
        public void AddProvider(ICommunicationProvider provider)
        {
            string key = provider.ProviderName;

            if (key.IsNullOrWhiteSpace()) throw new NullReferenceException($"The {nameof(provider.ProviderName)} is null, not invaild.");

            if (providers.ContainsKey(key)) throw new InvalidOperationException($"The {key} provider is existed.");

            providers.Add(key, provider);
        }

        /// <summary>
        /// Gets the provider.
        /// </summary>
        /// <param name="providerName">Name of the provider.</param>
        /// <returns></returns>
        public ICommunicationProvider GetProvider(string providerName)
        {
            if (providerName != null && providers.ContainsKey(providerName)) return providers[providerName];

            return null;
        }

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            foreach (var item in providers.Values)
            {
                ICommunicationProvider provider = item as ICommunicationProvider;

                provider.Start();
            }
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            foreach (var item in providers.Values)
            {
                ICommunicationProvider provider = item as ICommunicationProvider;

                provider.Stop();
            }
        }

        /// <summary>
        /// Restarts this instance.
        /// </summary>
        public void Restart()
        {
            foreach (var item in providers.Values)
            {
                ICommunicationProvider provider = item as ICommunicationProvider;

                provider.Restart();
            }
        }

        /// <summary>
        /// Registers the container.
        /// </summary>
        /// <param name="container">The container.</param>
        public void RegisterContainer(IModuleContainer container) => ModuleManager.RegisterContainer(container);

        /// <summary>
        /// Registers the container.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void RegisterContainer<T>() where T : IModuleContainer, new() => ModuleManager.RegisterContainer<T>();

        /// <summary>
        /// Builders this instance.
        /// </summary>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public ICommunicationManager Builder(params Assembly[] assemblies)
        {
            Assembly[] _assemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies().Select(x => Assembly.Load(x.FullName)).Concat(new Assembly[] { Assembly.GetEntryAssembly() }).Concat(assemblies).Distinct().ToArray();

            Type[] types = _assemblies.SelectMany(x => x.GetDefinedTypes(xx => xx.IsClass && xx.IsPublic && xx.IsSubclassOf(typeof(CommunicationModuleBase))))?.ToArray();

            ModuleManager.AddModule(types);

            foreach (var item in providers.Keys)
            {
                providers[item].RegisterModules(types);
            }

            return this;
        }
    }
}
